
import torch.nn as nn

from training.models.resnet import BasicBlock

__all__ = ['resnextcolorspace']

class ResNeXtBlock(nn.Module):

    def __init__(self,in_places,places, stride=1,downsampling=False, expansion = 2, cardinality=32):
        super(ResNeXtBlock,self).__init__()
        self.expansion = expansion
        self.downsampling = downsampling

        self.bottleneck = nn.Sequential(
            nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False, groups=cardinality),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_channels=places, out_channels=places * self.expansion, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(places * self.expansion),
        )

        if self.downsampling:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels=in_places, out_channels=places * self.expansion, kernel_size=1, stride=stride,bias=False),
                nn.BatchNorm2d(places * self.expansion)
            )
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        residual = x
        out = self.bottleneck(x)

        if self.downsampling:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)
        return out

class ResnextSpaceGenerator(nn.Module):

    def __init__(self, inplanes, planes):
        super(ResnextSpaceGenerator, self).__init__()
        self.conv1 = nn.Conv2d(inplanes, 64, 3, 1, 1)
        self.act1 = nn.LeakyReLU(True)

        self.block1 = ResNeXtBlock(in_places=64, places=32)
        self.block2 = ResNeXtBlock(in_places=64, places=32)
        self.block3 = ResNeXtBlock(in_places=64, places=32)
        self.block4 = ResNeXtBlock(in_places=64, places=32)
        self.block5 = ResNeXtBlock(in_places=64, places=32)

        self.conv2 = nn.Conv2d(64, 64, 3, 1, 1)
        self.bn2 = nn.BatchNorm2d(64)

        self.conv3 = nn.Conv2d(64, 64, 3, 1, 1)
        self.act3 = nn.LeakyReLU(True)
        self.conv4 = nn.Conv2d(64, planes, 3, 1, 1)

    def forward(self, x):
        x = self.conv1(x)
        x = self.act1(x)
        identity = x

        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.block5(x)

        x = self.conv2(x)
        x = self.bn2(x)
        x = x + identity

        x = self.conv3(x)
        x = self.act3(x)
        x = self.conv4(x)

        return x


class ResnextColorSpace(nn.Module):

    def __init__(self, feature_extractor, in_channels, num_classes, pretrained=False):
        super(ResnextColorSpace, self).__init__()
        self.compact = ResnextSpaceGenerator(in_channels, 3)
        self.features = feature_extractor(pretrained=pretrained, num_classes=num_classes)

    def forward(self, x):
        x = self.compact(x)
        x = self.features(x)
        return x


def resnextcolorspace(feature_extractor, in_channels=3, num_classes=1000, pretrained=False):
    model = ResnextColorSpace(feature_extractor, in_channels=in_channels, num_classes=num_classes, pretrained=pretrained)
    model.default_cfg = model.features.default_cfg
    return model
